(ns fxbattle.server.protobuilder
 (:import (fxbattle.protos 
            SharedProtos
            SharedProtos$ProtoLocation
            SharedProtos$RgbColor
            SharedProtos$Path
            
            ServerProtos
            ServerProtos$Player
            ServerProtos$RespondToPlayRequest
            ServerProtos$FlowPathChange
            ServerProtos$PathAvailabilityChanged
            ServerProtos$NodeOccupationChanged
            ServerProtos$NodeChange
            ServerProtos$NodeType
            ServerProtos$NodeType$KindOfNode
            ServerProtos$StateUpdate))
 (:require fxbattle.server.core)
 (:gen-class))


;;;
(defn- make-Location
  "Passed a cell struct ref, returns a ServerProtos$Location"
  [{x :x y :y}]
  (.build (.setLocY (.setLocX (. SharedProtos$ProtoLocation newBuilder) x) y)))

;;;
(defn- make-RgbColor
  "Passed an color structure, returns a ServerProtos$RgbColor"
  [{red :red blue :blue green :green}]
  (let [rgbColor (. SharedProtos$RgbColor newBuilder)]
    (.setRed rgbColor red)
    (.setBlue rgbColor blue)
    (.setGreen rgbColor green)
    (.build rgbColor)))

;;;
(defn- make-Path
  "Pased a path struct ref, returns a ServerProtos$Path"
  [{origin :origin destination :destination}]
  (let [spath (. SharedProtos$Path newBuilder)]
    (.setOrigin spath (make-Location origin))
    (.setDestination spath (make-Location destination))
    (.build spath)))

;;;
(defn make-Player
  "Passed a player structure, returns a ServerProtos$Player"
  [{name :name color :color}]
  (.build (.setColor (.setName (. ServerProtos$Player newBuilder) name)
            (make-RgbColor color))))

;;;
(defn make-RespondToPlayRequest
  "Passed a player structure, returns a ServerProtos$RespondToPlayRequest"
  [player]
  (.build (.setYou (. ServerProtos$RespondToPlayRequest newBuilder)
            (make-Player player))))

;;; def
(defn make-FlowPathChange 
  "Passed a path struct ref and a boolean indication of whether it is flowing,
returns a ServerProtos$FlowPathChange"
  [path player]
  (.build
    (.setIsFlowing
      (.setPath (. ServerProtos$FlowPathChange newBuilder) (make-Path path))
      (contains? @(:players path) player))))

;;; def
(defn make-PathAvailabilityChanged 
  "Passed a path struct ref and a boolean inication of whether it is available,
returns a ServerProtos$PathAvailabilityChanged"
  [path]
  (.build
    (.setIsAvailable
      (.setToChange (. ServerProtos$PathAvailabilityChanged newBuilder) (make-Path path))
      true)))

;;; def
(defn make-NodeOccupationChanged
  "Passed a cell struct ref, returns a ServerProtos$NodeOccupationChanged"
  [cell]
  (when (:owner @(:army cell))
    (let [noc (. ServerProtos$NodeOccupationChanged newBuilder)
          army @(:army cell)]
      (.setLoc noc (make-Location cell))
      (.setOwner noc (make-Player (:owner army)))
      (.setSize noc (:troops army))
      (.build noc))))

;;; def
(defn make-NodeChange 
  "Passed a cell struct ref, returns a ServerProtos$NodeChange"
  [cell]
  (let [nc (. ServerProtos$NodeChange newBuilder)
        kind (if (:base cell)
               (. ServerProtos$NodeType$KindOfNode BASE)
               (. ServerProtos$NodeType$KindOfNode NORMAL))]
    (.setLoc nc (make-Location cell))
    (.setElevation nc (:z cell))
    (.setType nc (.setKind (. ServerProtos$NodeType newBuilder) kind))
    (.build nc)))

;;;
(defn make-InitialStateUpdate 
  "Passed a sequence of ServerProtos$NodeChanged and a player struct,
returns a ServerProtos$StateUpdate"
  [game]
  (.build
    (.addAllPathChange
      (.addAllNodeChange
        (. ServerProtos$StateUpdate newBuilder)
        (map #'make-NodeChange (:cell-list game)))
      (map #'make-PathAvailabilityChanged (:path-list game)))))

;;;
(defn make-StateUpdate 
  "Passed a game struct, builds and returns a ServerProtos$StateUpdate"
  [player game]
  (.build
    (.addAllArmyChange
      (. ServerProtos$StateUpdate newBuilder)
      (remove #'nil? (map #'make-NodeOccupationChanged (:cell-list game))))))

;;;
(defn make-FlowStateUpdate
  "Passed a game struct, builds and returns a ServerProtos$StateUpdate"
  [path player]
  (.build
    (.addFlowChange
      (. ServerProtos$StateUpdate newBuilder)
      (make-FlowPathChange path player))))

;;;
(defn set-path
  "Takes a proto path, creates a game path and adds it as active in the game state."
  [path active player game]
  (let [orig (.getOrigin path) dest (.getDestination path)]
    (fxbattle.server.core/set-path
      (.getLocX orig) (.getLocY orig)
      (.getLocX dest) (.getLocY dest)
      active player game)))
